home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / pyshared / apt / gtk / widgets.py
Encoding:
Python Source  |  2009-03-30  |  14.4 KB  |  441 lines

  1. #!/usr/bin/env python
  2. #
  3. # Copyright (c) 2004-2005 Canonical
  4. #
  5. # Authors: Michael Vogt <michael.vogt@ubuntu.com>
  6. #          Sebastian Heinlein <glatzor@ubuntu.com>
  7. #          Julian Andres Klode <jak@debian.org>
  8. #
  9. #  This program is free software; you can redistribute it and/or
  10. #  modify it under the terms of the GNU General Public License as
  11. #  published by the Free Software Foundation; either version 2 of the
  12. #  License, or (at your option) any later version.
  13. #
  14. #  his program is distributed in the hope that it will be useful,
  15. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17. #  GNU General Public License for more details.
  18. #
  19. #  You should have received a copy of the GNU General Public License
  20. #  along with this program; if not, write to the Free Software
  21. #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  22. #  USA
  23. """GObject-powered progress classes and a GTK+ status widget."""
  24.  
  25. from gettext import gettext as _
  26. import os
  27. import time
  28.  
  29. import pygtk
  30. pygtk.require('2.0')
  31. import gtk
  32. import glib
  33. import gobject
  34. import pango
  35. import vte
  36.  
  37. import apt
  38. import apt_pkg
  39.  
  40.  
  41. def mksig(params=(), run=gobject.SIGNAL_RUN_FIRST, rettype=gobject.TYPE_NONE):
  42.     """Simplified Create a gobject signal.
  43.  
  44.     This allows us to write signals easier, because we just need to define the
  45.     type of the parameters (in most cases).
  46.  
  47.     ``params`` is a tuple which defines the types of the arguments.
  48.     """
  49.     return (run, rettype, params)
  50.  
  51.  
  52. class GOpProgress(gobject.GObject, apt.progress.OpProgress):
  53.     """Operation progress with GObject signals.
  54.  
  55.     Signals:
  56.  
  57.         * status-changed(str: operation, int: percent)
  58.         * status-started()  - Not Implemented yet
  59.         * status-finished()
  60.  
  61.     """
  62.  
  63.     __gsignals__ = {"status-changed": mksig((str, int)),
  64.                     "status-started": mksig(),
  65.                     "status-finished": mksig()}
  66.  
  67.     def __init__(self):
  68.         apt.progress.OpProgress.__init__(self)
  69.         gobject.GObject.__init__(self)
  70.         self._context = glib.main_context_default()
  71.  
  72.     def update(self, percent):
  73.         """Called to update the percentage done"""
  74.         self.emit("status-changed", self.op, percent)
  75.         while self._context.pending():
  76.             self._context.iteration()
  77.  
  78.     def done(self):
  79.         """Called when all operation have finished."""
  80.         self.emit("status-finished")
  81.  
  82.  
  83. class GInstallProgress(gobject.GObject, apt.progress.InstallProgress):
  84.     """Installation progress with GObject signals.
  85.  
  86.     Signals:
  87.  
  88.         * status-changed(str: status, int: percent)
  89.         * status-started()
  90.         * status-finished()
  91.         * status-timeout()
  92.         * status-error()
  93.         * status-conffile()
  94.  
  95.     """
  96.     # Seconds until a maintainer script will be regarded as hanging
  97.     INSTALL_TIMEOUT = 5 * 60
  98.  
  99.     __gsignals__ = {"status-changed": mksig((str, int)),
  100.                     "status-started": mksig(),
  101.                     "status-timeout": mksig(),
  102.                     "status-error": mksig(),
  103.                     "status-conffile": mksig(),
  104.                     "status-finished": mksig()}
  105.  
  106.     def __init__(self, term):
  107.         apt.progress.InstallProgress.__init__(self)
  108.         gobject.GObject.__init__(self)
  109.         self.finished = False
  110.         self.time_last_update = time.time()
  111.         self.term = term
  112.         reaper = vte.reaper_get()
  113.         reaper.connect("child-exited", self.childExited)
  114.         self.env = ["VTE_PTY_KEEP_FD=%s" % self.writefd,
  115.                     "DEBIAN_FRONTEND=gnome",
  116.                     "APT_LISTCHANGES_FRONTEND=gtk"]
  117.         self._context = glib.main_context_default()
  118.  
  119.     def childExited(self, term, pid, status):
  120.         """Called when a child process exits"""
  121.         self.apt_status = os.WEXITSTATUS(status)
  122.         self.finished = True
  123.  
  124.     def error(self, pkg, errormsg):
  125.         """Called when an error happens.
  126.  
  127.         Emits: status-error()
  128.         """
  129.         self.emit("status-error")
  130.  
  131.     def conffile(self, current, new):
  132.         """Called during conffile.
  133.  
  134.         Emits: status-conffile()
  135.         """
  136.         self.emit("status-conffile")
  137.  
  138.     def startUpdate(self):
  139.         """Called when the update starts.
  140.  
  141.         Emits: status-started()
  142.         """
  143.         self.emit("status-started")
  144.  
  145.     def finishUpdate(self):
  146.         """Called when the update finished.
  147.  
  148.         Emits: status-finished()
  149.         """
  150.         self.emit("status-finished")
  151.  
  152.     def statusChange(self, pkg, percent, status):
  153.         """Called when the status changed.
  154.  
  155.         Emits: status-changed(status, percent)
  156.         """
  157.         self.time_last_update = time.time()
  158.         self.emit("status-changed", status, percent)
  159.  
  160.     def updateInterface(self):
  161.         """Called periodically to update the interface.
  162.  
  163.         Emits: status-timeout() [When a timeout happens]
  164.         """
  165.         apt.progress.InstallProgress.updateInterface(self)
  166.         while self._context.pending():
  167.             self._context.iteration()
  168.         if self.time_last_update + self.INSTALL_TIMEOUT < time.time():
  169.             self.emit("status-timeout")
  170.  
  171.     def fork(self):
  172.         """Fork the process."""
  173.         return self.term.forkpty(envv=self.env)
  174.  
  175.     def waitChild(self):
  176.         """Wait for the child process to exit."""
  177.         while not self.finished:
  178.             self.updateInterface()
  179.         return self.apt_status
  180.  
  181.  
  182. class GDpkgInstallProgress(apt.progress.DpkgInstallProgress, GInstallProgress):
  183.     """An InstallProgress for local installations.
  184.  
  185.     Signals:
  186.  
  187.         * status-changed(str: status, int: percent)
  188.         * status-started()  - Not Implemented yet
  189.         * status-finished()
  190.         * status-timeout() - When the maintainer script hangs
  191.         * status-error() - When an error happens
  192.         * status-conffile() - On Conffile
  193.     """
  194.  
  195.     def run(self, debfile):
  196.         """Install the given package."""
  197.         apt.progress.DpkgInstallProgress.run(self, debfile)
  198.  
  199.     def updateInterface(self):
  200.         """Called periodically to update the interface.
  201.  
  202.         Emits: status-timeout() [When a timeout happens]"""
  203.         apt.progress.DpkgInstallProgress.updateInterface(self)
  204.         if self.time_last_update + self.INSTALL_TIMEOUT < time.time():
  205.             self.emit("status-timeout")
  206.  
  207.  
  208. class GFetchProgress(gobject.GObject, apt.progress.FetchProgress):
  209.     """A Fetch Progress with GObject signals.
  210.  
  211.     Signals:
  212.  
  213.         * status-changed(str: description, int: percent)
  214.         * status-started()
  215.         * status-finished()
  216.     """
  217.  
  218.     __gsignals__ = {"status-changed": mksig((str, int)),
  219.                     "status-started": mksig(),
  220.                     "status-finished": mksig()}
  221.  
  222.     def __init__(self):
  223.         apt.progress.FetchProgress.__init__(self)
  224.         gobject.GObject.__init__(self)
  225.         self._continue = True
  226.         self._context = glib.main_context_default()
  227.  
  228.     def start(self):
  229.         self.emit("status-started")
  230.  
  231.     def stop(self):
  232.         self.emit("status-finished")
  233.  
  234.     def cancel(self):
  235.         self._continue = False
  236.  
  237.     def pulse(self):
  238.         apt.progress.FetchProgress.pulse(self)
  239.         currentItem = self.currentItems + 1
  240.         if currentItem > self.totalItems:
  241.             currentItem = self.totalItems
  242.         if self.currentCPS > 0:
  243.             text = (_("Downloading file %(current)li of %(total)li with "
  244.                       "%(speed)s/s") % \
  245.                       {"current": currentItem,
  246.                        "total": self.totalItems,
  247.                        "speed": apt_pkg.SizeToStr(self.currentCPS)})
  248.         else:
  249.             text = (_("Downloading file %(current)li of %(total)li") % \
  250.                       {"current": currentItem,
  251.                        "total": self.totalItems})
  252.         self.emit("status-changed", text, self.percent)
  253.         while self._context.pending():
  254.             self._context.iteration()
  255.         return self._continue
  256.  
  257.  
  258. class GtkAptProgress(gtk.VBox):
  259.     """Graphical progress for installation/fetch/operations.
  260.  
  261.     This widget provides a progress bar, a terminal and a status bar for
  262.     showing the progress of package manipulation tasks.
  263.     """
  264.  
  265.     def __init__(self):
  266.         gtk.VBox.__init__(self)
  267.         self.set_spacing(6)
  268.         # Setup some child widgets
  269.         self._expander = gtk.Expander(_("Details"))
  270.         self._terminal = vte.Terminal()
  271.         #self._terminal.set_font_from_string("monospace 10")
  272.         self._expander.add(self._terminal)
  273.         self._progressbar = gtk.ProgressBar()
  274.         # Setup the always italic status label
  275.         self._label = gtk.Label()
  276.         attr_list = pango.AttrList()
  277.         attr_list.insert(pango.AttrStyle(pango.STYLE_ITALIC, 0, -1))
  278.         self._label.set_attributes(attr_list)
  279.         self._label.set_ellipsize(pango.ELLIPSIZE_END)
  280.         self._label.set_alignment(0, 0)
  281.         # add child widgets
  282.         self.pack_start(self._progressbar, False)
  283.         self.pack_start(self._label, False)
  284.         self.pack_start(self._expander, False)
  285.         # Setup the internal progress handlers
  286.         self._progress_open = GOpProgress()
  287.         self._progress_open.connect("status-changed", self._on_status_changed)
  288.         self._progress_open.connect("status-started", self._on_status_started)
  289.         self._progress_open.connect("status-finished",
  290.                                     self._on_status_finished)
  291.         self._progress_fetch = GFetchProgress()
  292.         self._progress_fetch.connect("status-changed", self._on_status_changed)
  293.         self._progress_fetch.connect("status-started", self._on_status_started)
  294.         self._progress_fetch.connect("status-finished",
  295.                                      self._on_status_finished)
  296.         self._progress_install = GInstallProgress(self._terminal)
  297.         self._progress_install.connect("status-changed",
  298.                                        self._on_status_changed)
  299.         self._progress_install.connect("status-started",
  300.                                        self._on_status_started)
  301.         self._progress_install.connect("status-finished",
  302.                                      self._on_status_finished)
  303.         self._progress_install.connect("status-timeout",
  304.                                      self._on_status_timeout)
  305.         self._progress_install.connect("status-error",
  306.                                      self._on_status_timeout)
  307.         self._progress_install.connect("status-conffile",
  308.                                      self._on_status_timeout)
  309.         self._progress_dpkg_install = GDpkgInstallProgress(self._terminal)
  310.         self._progress_dpkg_install.connect("status-changed",
  311.                                        self._on_status_changed)
  312.         self._progress_dpkg_install.connect("status-started",
  313.                                        self._on_status_started)
  314.         self._progress_dpkg_install.connect("status-finished",
  315.                                      self._on_status_finished)
  316.         self._progress_dpkg_install.connect("status-timeout",
  317.                                      self._on_status_timeout)
  318.         self._progress_dpkg_install.connect("status-error",
  319.                                      self._on_status_timeout)
  320.         self._progress_dpkg_install.connect("status-conffile",
  321.                                      self._on_status_timeout)
  322.  
  323.     def clear(self):
  324.         """Reset all status information."""
  325.         self._label.set_label("")
  326.         self._progressbar.set_fraction(0)
  327.         self._expander.set_expanded(False)
  328.  
  329.     @property
  330.     def open(self):
  331.         """Return the cache opening progress handler."""
  332.         return self._progress_open
  333.  
  334.     @property
  335.     def install(self):
  336.         """Return the install progress handler."""
  337.         return self._progress_install
  338.  
  339.     @property
  340.     def dpkg_install(self):
  341.         """Return the install progress handler for dpkg."""
  342.         return self._dpkg_progress_install
  343.  
  344.     @property
  345.     def fetch(self):
  346.         """Return the fetch progress handler."""
  347.         return self._progress_fetch
  348.  
  349.     def _on_status_started(self, progress):
  350.         """Called when something starts."""
  351.         self._on_status_changed(progress, _("Starting..."), 0)
  352.         while gtk.events_pending():
  353.             gtk.main_iteration()
  354.  
  355.     def _on_status_finished(self, progress):
  356.         """Called when something finished."""
  357.         self._on_status_changed(progress, _("Complete"), 100)
  358.         while gtk.events_pending():
  359.             gtk.main_iteration()
  360.  
  361.     def _on_status_changed(self, progress, status, percent):
  362.         """Called when the status changed."""
  363.         self._label.set_text(status)
  364.         if percent is None:
  365.             self._progressbar.pulse()
  366.         else:
  367.             self._progressbar.set_fraction(percent/100.0)
  368.         while gtk.events_pending():
  369.             gtk.main_iteration()
  370.  
  371.     def _on_status_timeout(self, progress):
  372.         """Called when timeout happens."""
  373.         self._expander.set_expanded(True)
  374.         while gtk.events_pending():
  375.             gtk.main_iteration()
  376.  
  377.     def cancel_download(self):
  378.         """Cancel a currently running download."""
  379.         self._progress_fetch.cancel()
  380.  
  381.     def show_terminal(self, expanded=False):
  382.         """Show the expander for the terminal.
  383.  
  384.         Show an expander with a terminal widget which provides a way
  385.         to interact with dpkg
  386.         """
  387.         self._expander.show()
  388.         self._terminal.show()
  389.         self._expander.set_expanded(expanded)
  390.         while gtk.events_pending():
  391.             gtk.main_iteration()
  392.  
  393.     def hide_terminal(self):
  394.         """Hide the expander with the terminal widget."""
  395.         self._expander.hide()
  396.         while gtk.events_pending():
  397.             gtk.main_iteration()
  398.  
  399.     def show(self):
  400.         """Show the Box"""
  401.         gtk.HBox.show(self)
  402.         self._label.show()
  403.         self._progressbar.show()
  404.         while gtk.events_pending():
  405.             gtk.main_iteration()
  406.  
  407.  
  408. def _test():
  409.     """Test function"""
  410.     import sys
  411.  
  412.     from apt.debfile import DebPackage
  413.  
  414.     win = gtk.Window()
  415.     apt_progress = GtkAptProgress()
  416.     win.set_title("GtkAptProgress Demo")
  417.     win.add(apt_progress)
  418.     apt_progress.show()
  419.     win.show()
  420.     cache = apt.cache.Cache(apt_progress.open)
  421.     pkg = cache["xterm"]
  422.     if pkg.isInstalled:
  423.         pkg.markDelete()
  424.     else:
  425.         pkg.markInstall()
  426.     apt_progress.show_terminal(True)
  427.     try:
  428.         cache.commit(apt_progress.fetch, apt_progress.install)
  429.     except Exception, exc:
  430.         print >> sys.stderr, "Exception happened:", exc
  431.     if len(sys.argv) > 1:
  432.         deb = DebPackage(sys.argv[1], cache)
  433.         deb.install(apt_progress.dpkg_install)
  434.     gtk.main()
  435.  
  436.  
  437. if __name__ == "__main__":
  438.     _test()
  439.  
  440. # vim: ts=4 et sts=4
  441.